6.4 表达式

方法和函数一样,除直接调用外,还可赋值给变量,或作为参数传递。依照具体引用方式的不同,可分为expression和value两种状态。

Method Expression

通过类型引用的method expression会被还原为普通函数样式,receiver是第一参数,调用时须显式传参。至于类型,可以是T或*\T,只要目标方法存在于该类型方法集中即可。

type N int
  
func(n N)test() { 
   fmt.Printf("test.n: %p, %d\n", &n,n) 
} 
  
func main() { 
   var n N=25
   fmt.Printf("main.n: %p, %d\n", &n,n) 
  
   f1:=N.test        //func(n N) 
   f1(n) 
  
   f2:= (*N).test         //func(n*N) 
   f2(&n)              // 按方法集中的签名传递正确类型的参数 
}

输出:

main.n:0xc82000a140,25
test.n:0xc82000a158,25
test.n:0xc82000a168,25

尽管*\N方法集包装的test方法receiver类型不同,但编译器会保证按原定义类型拷贝传值。

当然,也可直接以表达式方式调用。

func main() { 
   var n N=25
  
   N.test(n) 
    (*N).test(&n)            // 注意: *N须使用括号,以免语法解析错误 
}
 

Method Value

基于实例或指针引用的method value,参数签名不会改变,依旧按正常方式调用。

但当method value被赋值给变量或作为参数传递时,会立即计算并复制该方法执行所需的receiver对象,与其绑定,以便在稍后执行时,能隐式传入receiver参数。

type N int
  
func(n N)test() { 
   fmt.Printf("test.n: %p, %v\n", &n,n) 
} 
  
func main() { 
   var n N=100
   p:= &n
  
   n++ 
   f1:=n.test        // 因为test方法的receiver是N类型, 
                 // 所以复制n,等于101
   n++ 
   f2:=p.test        // 复制 *p,等于102
  
   n++ 
   fmt.Printf("main.n: %p, %v\n",p,n) 
  
   f1() 
   f2() 
}
 

输出:

main.n:0xc820076028,103
test.n:0xc820076060,101
test.n:0xc820076070,102

编译器会为method value生成一个包装函数,实现间接调用。至于receiver复制,和闭包的实现方法基本相同,打包成funcval,经由DX寄存器传递。

当method value作为参数时,会复制含receiver在内的整个method value。

func call(m func()) { 
   m() 
} 
  
func main() { 
   var n N=100
   p:= &n
  
   fmt.Printf("main.n: %p, %v\n",p,n) 
  
   n++ 
   call(n.test) 
  
   n++ 
   call(p.test) 
}
 

输出:

main.n:0xc82000a288,100
test.n:0xc82000a2c0,101
test.n:0xc82000a2d0,102

当然,如果目标方法的receiver是指针类型,那么被复制的仅是指针。

type N int
  
func(n*N)test() { 
   fmt.Printf("test.n: %p, %v\n",n, *n) 
} 
  
func main() { 
   var n N=100
   p:= &n
  
   n++ 
   f1:=n.test        // 因为test方法的receiver是 *N类型, 
                 // 所以复制 &n
   n++ 
   f2:=p.test        // 复制p指针 
  
   n++ 
   fmt.Printf("main.n: %p, %v\n",p,n) 
  
   f1()                // 延迟调用,n==103
   f2() 
}

输出:

main.n:0xc82000a298,103
test.n:0xc82000a298,103
test.n:0xc82000a298,103

只要receiver参数类型正确,使用nil同样可以执行。

type N int
  
func(N)value()    {} 
func(*N)pointer() {} 
  
func main() { 
   var p*N
  
   p.pointer()      //method value
    (*N)(nil).pointer()       //method value
    (*N).pointer(nil)     //method expression
  
    //p.value()         // 错误:invalid memory address or nil pointer dereference
}